Build a Chat Application with Redis Pub/Sub
Learn how to combine Redis Pub/Sub with WebSocket to build a collaborative chat service.
Pub/Sub with the go-redis client#
Conceptually, Redis Pub/Sub is quite simple. Here’s how it works with the go-redis client:
Line 1: Producers send data to a Redis channel.
Lines 3–4: Consumers subscribe to the channel to receive messages using
Subscribeand receive messages via a Go channel.Line 6: Here, we use
Unsubscribeand close the connection.
The application used in this lesson is a simplified version of a chat service.
Application overview#
The application uses a combination of Redis Pub/Sub and WebSocket to allow a horizontally scalable architecture. The application hosts a WebSocket server endpoint that clients (users) can connect to using a WebSocket client.
Here’s the high-level application flow:
A user connects to an endpoint provided by the application. If successful, this creates a WebSocket connection between the client and application (server).
When the user sends a chat message, it’s relayed over the WebSocket connection to the application.
The application publishes this message to the Redis channel.
Because the application is also subscribed to this channel, it receives this message and sends the application information about all the connected users (via the WebSocket connection that was established initially).
Note that the server also acts as a Pub/Sub client. We create a Redis Pub/Sub channel for broadcasting chat messages. The application sends data to this Redis channel and also receives data from the channel via a Pub/Sub subscription.
Use cases#
In this application, users can do the following actions:
Join the chat service
Send messages
Receive messages
Leave the chat service
What is WebSocket?#
WebSocket is a protocol defined by RFC 6455, which allows full-duplex communication between a client and server over a single TCP connection.
Full-duplex communication implies that:
Either the client or server can send messages.
Messages can be exchanges simultaneously
It’s an improvement over other communication patterns like long polling, which is used by HTTP-based solutions and is heavily used in building real-time applications.
The role of Redis Pub/Sub#
It’s possible to build this solution using only WebSocket and not Redis Pub/Sub. But it will only work with a single instance of the application. This implies that it won’t be possible to run multiple instances of the application for scalability.
In a scaled-out architecture, the users connected to the chat service might be associated with any of the application instances via the WebSocket connection, as shown in the diagram above. Without Redis Pub/Sub, only users connected to a specific instance will be able to exchange chat messages with each other. Redis Pub/Sub solves this problem by providing a way to broadcast messages to all the connected users, regardless of the application instance they’re connected to.
Code explanation#
Let’s walk through the application’s logic.
Imports#
Here are some of the package imports:
Line 4: In the
importsection, we importgorilla/websocket, which provides us the WebSocket server and client implementation in Go.
The init function#
The init function establishes a connection with Redis and is executed before the main function.
Line 1–3: We define the required variables.
Line 5: The
initfunction initializes theUsersmap. It contains the mapping of a user ID (who has joined the chat) to the corresponding*websocket.Connobject.
The main function#
The main function above is responsible for orchestrating all the functionality. This includes:
Line 9: After connecting with Redis and verifying it on lines 3–4, the
startChatBroadcasterfunction is invoked. It further starts agoroutine, which will be covered below.Line 11: The application has a single registered route.
Line 16: The server is started.
Line 24: After the server is started, the program blocks and waits for the application to exit/terminate.
The rest of the logic in the main function deals with the clean-up process after application exits. These include the following actions:
Line 28: Creating a
Context, which times out after ten seconds.Lines 32–34: Closing all the user WebSocket connections.
Lines 36–37: Unsubscribing and closing the Pub/Sub channel connection.
Line 39: Shutting down the server.
The chat HTTP handler#
Now, let’s take a look at the chat HTTP handler in the code below:
The chat method on line 5 of the code snippet above is an HTTP handler that allows a user to connect to the application over WebSocket and takes care of the user chat session while they are connected.
Line 11: First, the connection is upgraded to WebSocket.
Line 17: This WebSocket connection is added to the
Usersmap.Lines 20–21: The infinite
forloop is used to handle chat messages and terminate the session if the user disconnects (line 27).Line 36: The messages received over WebSocket are sent to the Redis Pub/Sub channel.
The startChatBroadcaster function#
Finally, we have the startChatBroadcaster function:
Line 2: The
startChatBroadcasterfunction starts agoroutine.Line 4: The
goroutinesubscribes to the Redis Pub/Sub channel.Lines 9–11: The
goroutinereceives messages and broadcasts them to all the connected users over the WebSocket connection.
Application testing#
You can take a look at the complete code for the application. Click the “Run” button in the code widget below to start the application:
/
Once that’s done, click + to open a new terminal tab and follow the instructions below to execute commands and test the application using curl.
Change to right directory:
Join the chat service as user1:
You should see an output along with the prompt (>).
In order to simulate another user (e.g. user2), click + to open a another terminal:
Let’s go back to the terminal where we started the application. We should see the following logs:
Now there are two users who have joined the service. They can exchange messages. To send and view these messages, perform the following actions:
Send a message from
user1, switch to the terminal where we joined the chat service asuser1and type in a text message, for example,Hello, how are you?Switch to the terminal where we joined the chat service as
user2. We should see the message thatuser1sent.From the same terminal, send a message as
user2, for example,Doing fine, thank you!Switch to the terminal where we joined the chat service as
user1. We should see the message thatuser2sent.
You can continue to learn and experiment with the chat application by adding more users. Finally, to exit the application, press “CTRL + C” in the first terminal where the application was started.
Summary#
The demonstrated chat interactions are happening in real-time using a combination of Redis Pub/Sub and WebSocket. While a chat service was used as an example, the concepts covered in this lesson can be applied to real-time collaborative applications in general.
Implement Worker Queues with Lists
Stream Processing with Redis Streams